# -*- coding: utf-8 -*-
# @Time    : 2025/10/5 14:01
# @Author  : 楚江涛
# @FileName: container.py
# @Software: PyCharm

"""
apps/workflow/wiring/container.py
-----------------------
应用装配（Composition Root）

职责：
- 构建并暴露全局可用的基础设施实例：
  * 事件总线（EventBus）—— 用于 SSE/长轮询推送
  * 状态存储（StateStore）—— /status 的快照来源
  * 代码执行器（CodeRunner）—— 执行“代码节点”
  * 编排服务（OrchestratorService）—— API 与执行器之间的门面
- 读取 Django settings 进行参数化（并发、超时、队列大小等）
- 提供优雅关闭（shutdown）方法

说明：
- 默认使用“内存实现”，适合本地与单进程调试；
- 若后续切换到 Redis/DB/容器沙箱，只需修改此文件的装配（依赖倒置）。
"""

from __future__ import annotations

import asyncio
from typing import Dict, Any, Optional

# 接口/服务
from execution.interfaces import CodeRunner
from application.orchestrator_service import OrchestratorService

# 基础设施（内存实现）
from infrastructure.eventbus_memory import MemoryEventBus
from infrastructure.state_memory import MemoryStateStore

# ============================================================
# 一、读取配置（提供合理默认值）
# ============================================================

WF_MAX_PARALLELISM: int = 8
WF_NODE_TIMEOUT_SEC: Optional[float] = 600
WF_SSE_HEARTBEAT_SEC: int = 20
WF_SSE_RETRY_MS: int = 3000
WF_EVENTBUS_MAX_QUEUE: int = 1000

# ============================================================
# 二、事件总线 & 状态存储（内存实现）
# ============================================================

eventbus = MemoryEventBus(max_queue_size=WF_EVENTBUS_MAX_QUEUE)
state = MemoryStateStore()

# ============================================================
# 三、代码执行器（CodeRunner）装配
#    优先尝试从 app.execution.runners 导入 InlineRunner；
#    若缺失，则回退到 MinimalInlineRunner（内联安全受限执行）。
# ============================================================

try:
    # 可替换为 SubprocessRunner / ContainerRunner 等更安全实现
    from app.execution.runners import InlineRunner  # type: ignore

    runner: CodeRunner = InlineRunner()
except Exception:
    # ---- 回退：极简内联执行器（安全性有限，仅用于本地 demo） ----
    import builtins
    import types
    import asyncio as _asyncio
    import json as _json
    import time as _time
    import math as _math
    import re as _re

    class MinimalInlineRunner(CodeRunner):
        """
        极简内联 CodeRunner：
        - 期望源码中定义：async def main(args: Args) -> dict
        - 允许受限导入（白名单），并预注入常用模块对象到 globals
        - 注意：生产环境请使用子进程/容器沙箱替代
        """

        # 允许导入的模块白名单（按需增减）
        _ALLOWED_MODULES = {
            "asyncio", "json", "time", "math", "re",
        }

        @staticmethod
        def _limited_import(name, globals=None, locals=None, fromlist=(), level=0):
            root = name.split(".", 1)[0]
            if root not in MinimalInlineRunner._ALLOWED_MODULES:
                raise ImportError(f"禁止导入模块: {name}")
            return builtins.__import__(name, globals, locals, fromlist, level)

        _ALLOWED_BUILTINS = {
            # 常用安全内建
            "len": builtins.len,
            "range": builtins.range,
            "min": builtins.min,
            "max": builtins.max,
            "sum": builtins.sum,
            "str": builtins.str,
            "int": builtins.int,
            "float": builtins.float,
            "bool": builtins.bool,
            "dict": builtins.dict,
            "list": builtins.list,
            "set": builtins.set,
            "tuple": builtins.tuple,
            "print": builtins.print,
            "enumerate": builtins.enumerate,
            "zip": builtins.zip,
            "abs": builtins.abs,
            "round": builtins.round,
            "Exception": builtins.Exception,
            # 关键：受限导入
            "__import__": _limited_import.__func__,  # 绑定为函数对象
        }

        async def run(
            self,
            node_id: str,
            code: str,
            params: Dict[str, Any],
            *,
            timeout: Optional[float] = None,
            env: Optional[Dict[str, str]] = None,
            metadata: Optional[Dict[str, Any]] = None,
        ) -> Dict[str, Any]:
            # 1) 准备受限命名空间（builtins + 预注入模块）
            globals_env: Dict[str, Any] = {
                "__builtins__": self._ALLOWED_BUILTINS,
                # 预注入常用模块（即便不 import 也可直接用）
                "asyncio": _asyncio,
                "json": _json,
                "time": _time,
                "math": _math,
                "re": _re,
            }
            locals_env: Dict[str, Any] = {}

            # 入参协议：Args.params = <字典>
            class Args:
                def __init__(self, p: Dict[str, Any]):
                    # 仅保留“干净”的 dict；避免用户修改外层对象
                    self.params = dict(p or {})

            Output = dict
            globals_env.update({"Args": Args, "Output": Output})

            # 2) 装载用户代码
            exec(code or "", globals_env, locals_env)
            main = locals_env.get("main") or globals_env.get("main")
            if not main or not asyncio.iscoroutinefunction(main):
                raise RuntimeError("代码节点必须定义 `async def main(args: Args) -> dict`")

            # 3) 运行（注意：Args 传参是 params，而非 {'params': params}）
            coro = main(Args(params))
            if timeout and timeout > 0:
                result = await asyncio.wait_for(coro, timeout=timeout)
            else:
                result = await coro

            if not isinstance(result, dict):
                raise RuntimeError("`main` 的返回值必须是 dict")
            return result

    runner = MinimalInlineRunner()

# ============================================================
# 四、编排服务（唯一对外服务门面）
# ============================================================

orchestrator = OrchestratorService(
    events=eventbus,
    state=state,
    runner=runner,
    max_parallelism=WF_MAX_PARALLELISM,
    node_timeout=WF_NODE_TIMEOUT_SEC,
)

# ============================================================
# 五、对外导出便捷函数（可选）
# ============================================================

def get_orchestrator() -> OrchestratorService:
    """获取编排服务单例"""
    return orchestrator


def get_eventbus() -> MemoryEventBus:
    """获取事件总线实例（内存实现）"""
    return eventbus


def get_state_store() -> MemoryStateStore:
    """获取状态存储实例（内存实现）"""
    return state


def get_code_runner() -> CodeRunner:
    """获取代码执行器实例"""
    return runner


# ============================================================
# 六、优雅关闭（进程退出/测试 Teardown）
# ============================================================

def shutdown() -> None:
    """
    优雅关闭：释放事件总线、执行器可能持有的资源。
    注意：内存实现通常无需特殊清理，此处预留扩展。
    """
    close = getattr(eventbus, "close", None)
    if callable(close):
        try:
            close()
        except Exception:
            pass

    # 若 runner/状态存储有 close()，也在此调用
    for comp in (runner, state):
        close = getattr(comp, "close", None)
        if callable(close):
            try:
                close()
            except Exception:
                pass
